package org.sgx.gwteditors.client.impl1.complex;

import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.sgx.gwteditors.client.editor.Editor;
import org.sgx.gwteditors.client.editor.error.EditorError;
import org.sgx.gwteditors.client.editor.error.EditorErrorManager;
import org.sgx.gwteditors.client.editor.event.EditorEventManager;
import org.sgx.gwteditors.client.editor.event.ValueChangeListener;
import org.sgx.gwteditors.client.editor.props.PropertyHaver;
import org.sgx.gwteditors.client.editor.props.PropertyInfo;
import org.sgx.gwteditors.client.impl1.EditorManager1;
import org.sgx.gwteditors.client.util.GUIUtil;

import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.dom.client.Document;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.ListDataProvider;

/**
 * a Better PH table editor than version 1, this time with checkboxes for selecting items and buttons for removing / adding. No item reordering supprot.
 * 
 * @author sg
 */
public class PropertyHaverTableEditor2<T extends PropertyHaver> extends VerticalPanel implements Editor<List<T>> {

	static Logger logger = Logger.getLogger(PropertyHaverTableEditor2.class + "");
	Map<Integer, Editor<T>> eds = null;
	private List<T> model;
	private CellTable<T> table;
	private Map<String, Namer> namers;
	private Map<Integer, Boolean> selection;

	public interface Namer {
		String toString(Object t);
	}

	public static interface EditorClickHandler {
		void call(Object val, int column, int index);
	}
	EditorClickHandler clickHandler;
	
	public Namer DEFAULT_NAMER = new Namer() {
		@Override
		public String toString(Object t) {
			return t == null ? "false" : t + "";
		}
	};
	public class BetterComparator<T extends PropertyHaver> implements Comparator<T> {
		String prop;
		private BetterComparator(String prop) {
			super();
			this.prop = prop;
		}

		@Override
		public int compare(T o1, T o2) {			
			String p1 = DEFAULT_NAMER.toString(o1.propertyGet(prop));
			String p2 = DEFAULT_NAMER.toString(o2.propertyGet(prop));
			return p1.compareTo(p2);
		}
		
	}
	private Map<String, Comparator> columnComparators;
	protected DialogBox currentDialogBox;
	protected CellPreviewEvent<T> currentRowClickEvent;
	private FlowPanel buttonPanel;
	private Column<T, Boolean> selectionColumn;

	public PropertyHaverTableEditor2(Map<String, Namer> namers, Map<String, Comparator> columnComparators) {
		super();
		this.namers = namers; 
		eds = new HashMap<Integer, Editor<T>>();
		this.columnComparators = columnComparators;
		selection=new HashMap<Integer, Boolean>(); 
	}

	public PropertyHaverTableEditor2() {
		this(new HashMap<String, Namer>(), new HashMap<String, Comparator>());
	}

	@Override
	public void addValueChangeListener(ValueChangeListener<List<T>> l) {
		EditorEventManager.<List<T>> getInstance().addValueChangeListener(this, l);
	}

	@Override
	public void removeValueChangeListener(ValueChangeListener<List<T>> l) {
		EditorEventManager.<List<T>> getInstance().addValueChangeListener(this, l);
	}

	@Override
	public List<EditorError<List<T>>> getErrors() {
		return EditorErrorManager.<List<T>> getInstance().getErrors(this);
	}

	@Override
	public List<T> flush() {
		//TODO
		return model;
	}

	private void buildTableFor(T ph) {		
		
//		createButtonPanel();
		if (table != null) {
			// TODO: clear previous data
		} else {
			table = new CellTable<T>();
//			table.setWidth("100%"); 
			this.add(table);
		}	

		ListDataProvider<T> dataProvider = new ListDataProvider<T>();

		// Connect the table to the data provider
		dataProvider.addDataDisplay(table);
		List<T> list = dataProvider.getList();
		final ListHandler<T> columnSortHandler = new ListHandler<T>(list);
		
		//first define a first table column for selection checkbox
		selectionColumn = new Column<T, Boolean>(new CheckboxCell()) { 
			@Override
			public Boolean getValue(T object) {
				return (Boolean) null;
			}
		};
		selectionColumn.setFieldUpdater(new FieldUpdater<T, Boolean>() {

			@Override
			public void update(int index, T object, Boolean value) {
				selection.put(index, value); 
//				System.out.println("UPDATE: "+index+" - "+value);
			}
		}); 
		table.addColumn(selectionColumn, "");			
		
		// define other table columns
		for (final String pname : ph.getProperties().keys()) {
//			PropertyInfo prop = ph.getProperties().get(pname);
			final Namer namer = getNamer(pname);
			TextColumn<T> col = new TextColumn<T>() {
				@Override
				public String getValue(T t) {
					String label = namer.toString(t.propertyGet(pname));
					// System.out.println("getting "+pname+": "+t.getProperty(pname));
					return label;
					// return t.getProperty(pname)==null? "false" : t.getProperty(pname)+""; // do this better - use a custom LabelPrinter class
				}
			};
			table.addColumn(col, pname);
			
			// column sorting
			col.setSortable(true);
			Comparator<T> colComparator = new BetterComparator(pname);
			if (columnComparators != null && columnComparators.get(pname) != null) {
				colComparator = columnComparators.get(pname);
			}
			columnSortHandler.setComparator(col, colComparator);

		}
		table.addColumnSortHandler(columnSortHandler);

		// row click handlers
		table.addCellPreviewHandler(new CellPreviewEvent.Handler<T>() {
			@Override
			public void onCellPreview(CellPreviewEvent<T> event) {
				currentRowClickEvent = event; 
				boolean isClick = "click".equals(event.getNativeEvent().getType());
				 if(!isClick)
					 return; 
				
				 if(clickHandler!=null) {
					 T value = event.getValue();					 
					 clickHandler.call(value, event.getColumn(), event.getIndex()); 
				 }
				 else {
					 doDefaultAction(event);
				 }
//				 String pname = val.getProperties().keys().get(event.getColumn());
//				 PropertyInfo prop = val.getProperties().get(pname); 
//				 Object pval = val.getProperty(pname); 
//				 System.out.println(isClick+" - model: "+val+", pname: "+pname+", value: "+DEFAULT_NAMER.toString(pval));
				 
//				 Editor<Object> subed = editors.get(name);
//					Object submodel = //getModel()
//							model.getProperty(name);							
//					System.out.println(getModel().getProperties().keys()+" - "+getModel().getProperties().get("mother")+" - HHHHH: propname: "+name+", subed: "+subed+", model: "+getModel()+", submodel: "+submodel);
//					subed.load(submodel);				 
			}
		});
		
//		System.out.println(table.getElement());

		// add the data

		for(T t : model) {
			list.add(t);
		}
//		for (int i = 0; i < model.length; i++) {
//			T t = model[i];
//			list.add(t);
//		}
		
//		String tid = DOMUtil.setId(table.getElement()); 
//		NodeList<Element> tds = DOMUtil.querySelectorAll(table.getElement(), "tr > td"); 
//		System.out.println(tds.getLength());
//		if(tds!=null) for (int i = 0; i < tds.getLength(); i++) {
//			Element td = tds.getItem(i);
//			td.getStyle().setCursor(Cursor.POINTER); 
//			td.setTitle("click to edit this row object"); 
//		}

	}

	protected void doDefaultAction(CellPreviewEvent<T> event) {
		 T val = event.getValue();
		 PropertyHaverEditor2<T> ed = new PropertyHaverEditor2<T>(); //(Editor<T>) Types.getInstance().newPropertyEditor(model, name);
			ed.load(val); 
			
			VerticalPanel vp = new VerticalPanel();
			vp.add(ed);
			FlowPanel fp = new FlowPanel();
			fp.add(new Button("Accept", new ClickHandler() {								
				@Override
				public void onClick(ClickEvent event) {
					if(currentDialogBox!=null)
						currentDialogBox.hide();
				}
			}));
			fp.add(new Button("Cancel", new ClickHandler() {								
				@Override
				public void onClick(ClickEvent event) {
					if(currentDialogBox!=null)
						currentDialogBox.hide();
				}
			}));
			vp.add(fp);
			currentDialogBox = GUIUtil.showWidgetDialog("Editing row "+currentRowClickEvent.getIndex(), vp);
		 
	}

//	private void createButtonPanel() {
//		if(buttonPanel!=null) 
//			return;
//		this.buttonPanel = new FlowPanel(); 
//		Button addButton = new Button("add");
//		buttonPanel.add(addButton);
//		addButton.addClickHandler(new ClickHandler() {			
//			@Override
//			public void onClick(ClickEvent event) {
//				doAdd();				
//			}
//		}); 
//		Button removeButton = new Button("remove");
//		buttonPanel.add(removeButton); 
//		removeButton.addClickHandler(new ClickHandler() {			
//			@Override
//			public void onClick(ClickEvent event) {
//				doRemove();				
//			}
//		}); 
//	}

//	protected void doRemove() {
//		List<T> selection = getSelectedModel();
////		Arrays.s
//	}

	public List<T> getSelectedModel() {
		List<T> l = new LinkedList<T>();
		for(Integer index : selection.keySet()) {
			if(selection.get(index)) {
				l.add(model.get(index)); 
			}
		}
//		for (T t : model) {
//			String val = "";
//			try {
//				val = selectionColumn.getValue(t)+"";
//			} catch (Exception e) {
//				val = "NULL("+e+")"; 
//			}
//			System.out.println("getSelectedModel: "+selectionColumn+" - t: "+t+" - val: "+val);
////			if (selectionColumn.getValue(t)) {
////				l.add(t);
////			}
//		}
		// for (int i = 0; i < model.length; i++) {
		// T m = model[i];
		// if(selectionColumn.getValue(m)) {
		// l.add(i);
		// }
		// }
		return l;
	}

//	protected void doAdd() {
//		// TODO Auto-generated method stub
//		
//	}

	private Namer getNamer(String pname) {
		return namers.get(pname) == null ? DEFAULT_NAMER : namers.get(pname);
	}

	public EditorClickHandler getClickHandler() {
		return clickHandler;
	}

	public void setClickHandler(EditorClickHandler clickHandler) {
		this.clickHandler = clickHandler;
	}

	@Override
	public void load(List<T> model) {

		if (model == null)
			return;

		this.model = model;

		if (model.size() == 0) {
			// TODO: print "empty array"
			return;
		}

		buildTableFor(model.get(0)); //

		// clear the table
		// clear();

		// this.imax = model.length;
		// for (int i = 0; i < model.length; i++) {
		// T o = model[i];
		// // System.out.println(o);
		// if (o == null)
		// continue;
		// Editor<T> ed = null;
		// try {
		// ed = Types.getInstance().newEditor(type);// EditorFactoryImpl.<Object>getInstance().getEditorForObject(o);
		// final Widget w = ed.getWidget();
		// ed.load(o);
		//
		// String name = namer.getName(o);
		//
		// /*
		// * due to a bug in the slider component http://code.google.com/p/gwt-slider-bar/issues/detail?id=5 we need to first open the DP ,. then add the content and then
		// * close the DP.
		// */
		// final DisclosurePanel dp = new DisclosurePanel(name);
		// // dp.setOpen(true);
		// dp.setAnimationEnabled(true);
		// dp.setOpen(false);
		// dp.addOpenHandler(new OpenHandler<DisclosurePanel>() {
		// @Override
		// public void onOpen(OpenEvent<DisclosurePanel> event) {
		// if (event.getTarget().isOpen()) {
		// dp.setContent(w);
		// }
		// }
		// });
		// // Element e = dp.getHeader().getElement();
		// // DOMUtil.addEventHandler(e, "click", new EventHandler() {
		// // @Override
		// // public void onEvent(Event event) {
		// // System.out.println("YYYYYY");
		// // }
		// // }, false);
		//
		// if (dp.getHeader() instanceof HasMouseDownHandlers) {
		// System.out.println("yes");
		// } else {
		// System.out.println("no: " + dp.getHeader().getClass());
		// }
		// if (vertical) {
		// setWidget(i, 1, dp);
		//
		// // add/remove buttons
		// HorizontalPanel hp = new HorizontalPanel();
		//
		// Anchor addButton = new Anchor("add");
		// Anchor rmButton = new Anchor("remove");
		// hp.add(addButton);
		// hp.add(new HTML("<span>&nbsp;</span>"));
		// hp.add(rmButton);
		// setWidget(i, 0, hp);
		// getCellFormatter().setVerticalAlignment(i, 0, HasVerticalAlignment.ALIGN_TOP);
		//
		// } else {
		// setWidget(1, i, dp);
		// }
		//
		// eds.put(i, ed);
		// } catch (EditorNotFound e) {
		// logger.log(Level.WARNING, " exception building editor for field " + i + ", class: " + o.getClass()
		// + " : " + o + ". Cause: " + e.getCause() + " - " + e.getMessage());
		// e.printStackTrace();
		// }
		// // this.jmax=model[i].length;
		// // for(int j=0; j<model[i].length; j++) {
		// // Object o = model[i][j];
		// // if(o==null)
		// // continue;
		// // Editor<Object> ed=null;
		// // try {
		// // ed = EditorFactoryImpl.<Object>getInstance().getEditorForObject(o);
		// // ed.load(o);
		// // setWidget(i, j, ed.getWidget());
		// // eds.put(i+","+j, ed);
		// // } catch (EditorNotFound e) {
		// // logger.log(Level.WARNING, " exception building editor for field "+i+
		// // ", "+j+", class: "+model[i][j].getClass()+" : "+o+
		// // ". Cause: "+e.getCause()+" - "+e.getMessage());
		// // e.printStackTrace();
		// // }
		// // }
		// }
	}

	@Override
	public Widget getWidget() {
		return this;
	}

	@Override
	public boolean isSimple() {
		return false;
	}

	@Override
	public String getEditorId() {
		return EditorManager1.getInstance().getEditorName(this);
	}

	@Override
	public String[] getEditorTypesSupported() {
		return null; // String[]{EditorFramework1.TYPE_};
	}

}
